package com.xcf.cloud.gateway.filter;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Component
@RefreshScope
public class RegisterFilter implements GlobalFilter, Ordered {

    @Value("${register.duration-limit}")
    private Integer durationLimit;
    @Value("${register.time-limit}")
    private Integer timeLimit;

    private final Map<String, Map<Long, Integer>> data = new ConcurrentHashMap<>();

    private Integer getTime(String ip) {
        if (!data.containsKey(ip)) {
            return 0;
        }
        Map<Long, Integer> map;
        synchronized (data) {
            map = new HashMap<>(data.get(ip));
        }
        if (map.isEmpty()) {
            return 0;
        }
        long now = System.currentTimeMillis();
        int num = 0;
        for (Map.Entry<Long, Integer> entry : map.entrySet()) {
            Long time = entry.getKey();
            if (now - time <= durationLimit * 60 * 1000) {
                num = num + entry.getValue();
            } else {
                data.get(ip).remove(time);
            }
        }
        return num;
    }

    private void recordRequest(String ip) {
        synchronized (data) {
            if (!data.containsKey(ip)) {
                data.put(ip, new ConcurrentHashMap<>());
            }
        }
        long now = System.currentTimeMillis();
        synchronized (data) {
            if (data.get(ip).containsKey(now)) {
                data.get(ip).computeIfPresent(now, (key, value) -> value++);
            } else {
                data.get(ip).put(now, 1);
            }
        }
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        System.out.println("timeLimit = " + timeLimit);
        ServerHttpRequest request = exchange.getRequest();
        String url = request.getURI().getPath();
        if (!url.contains("/api/user/register")) {
            return chain.filter(exchange);
        }
        String ip = getIP(request);
        Integer time = getTime(ip);
        if (time >= timeLimit) {
            ServerHttpResponse response = exchange.getResponse();
            response.setStatusCode(HttpStatus.SEE_OTHER);
            DataBuffer buffer = response.bufferFactory().wrap("您频繁进⾏注册，请求已被拒绝".getBytes());
            return response.writeWith(Mono.just(buffer));
        }
        recordRequest(ip);
        return chain.filter(exchange);
    }

    private String getIP(ServerHttpRequest request) {
        String ip = request.getHeaders().getFirst("x-forwarded-for");
        if (ip != null && ip.length() != 0 && !"unknown".equalsIgnoreCase(ip)) {
            // 多次反向代理后会有多个ip值，第一个ip才是真实ip
            if (ip.contains(",")) {
                ip = ip.split(",")[0];
            }
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeaders().getFirst("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeaders().getFirst("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeaders().getFirst("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeaders().getFirst("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeaders().getFirst("X-Real-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddress().getHostName();
        }
        return ip;
    }

    @Override
    public int getOrder() {
        return 1;
    }
}
